package cn.yunhe.spring.aop;

import java.util.Date;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;

@Component
@Aspect
public class Advices {
	/**
	 * 在cn.yunhe.spring.service.*Impl接口的每一个实现类的每一个方法开始之前执行一段代码
	 */
	@Before("execution(* cn.yunhe.spring.service.*Impl.*(..))")
	public void before(JoinPoint jp) {
		System.out.println("----------前置通知----------");
		System.out.println(jp.getSignature().getName());
	}

	/**
	 * 在execution(* cn.yunhe.spring.service.*.*接口的每一个实现类的每一个方法执行之后执行一段代码
	 * 无论该方法是否出现异常
	 */
	@After("execution(* cn.yunhe.spring.service.*.*(..))")
	public void after(JoinPoint jp) {
		System.out.println("----------最终通知----------");
	}

	/**
	 * 环绕通知需要携带ProceedingJoinPoint类型的参数
	 * 环绕通知类似于动态代理的全过程：ProceedingJoinPoint类型的参数可以决定是否执行目标方法。
	 * 而且环绕通知必须有返回值，返回值即为目标方法的返回值
	 */
	@Around("execution(* cn.yunhe.spring.service.*.*(..))")
	public Object logAround(ProceedingJoinPoint jp) {

		long start = System.currentTimeMillis();

		String className = jp.getThis().toString();
		String methodName = jp.getSignature().getName();
		System.out.println(
				"logAround开始:位于" + className + "调用" + methodName + "方法开始!现在时间是:" + new Date().toLocaleString());
		Object[] args = jp.getArgs();
		Object obj = null;
		try {
			obj = jp.proceed(args);
		} catch (Throwable e) {
			e.printStackTrace();
		}
		long end = System.currentTimeMillis();
		System.out.println("logAround结束:位于" + className + "调用" + methodName + "方法结束!现在时间是:"
				+ new Date().toLocaleString() + "\n耗时:" + (end - start));

		return obj;
	}

	/**
	 * 方法正常结束后执行的代码 返回通知是可以访问到方法的返回值的
	 */
	@AfterReturning(value = "declareJoinPointExpression()", returning = "result")
	public void afterReturning(JoinPoint joinPoint, Object result) {
		String methodName = joinPoint.getSignature().getName();
		System.out.println("The method " + methodName + " return with " + result);
	}

	/**
	 * 定义一个方法，用于声明切入点表达式。一般的，该方法中再不需要添加其他的代码 使用@Pointcut 来声明切入点表达式
	 * 后面的其他通知直接使用方法名直接引用方法名即可
	 */
	@Pointcut("execution(* cn.yunhe.spring.service.*.*(..))")
	public void declareJoinPointExpression() {

	}

	/**
	 * 在方法出现异常时会执行的代码 可以访问到异常对象，可以指定在出现特定异常时在执行通知代码
	 */
	@AfterThrowing(value = "declareJoinPointExpression()", throwing = "ex")
	public void afterThrowing(JoinPoint joinPoint, Exception ex) {
		String methodName = joinPoint.getSignature().getName();
		System.out.println("The method " + methodName + " occurs exception: " + ex);
	}

}
